查看原文
其他

Cocos Creator 探照灯效果 Shader 实现

COCOS 2022-06-10

Editor's Note

很多游戏效果通过简单的 Shader 实现往往能达到事半功倍的效果,尤其对 2D 游戏来说,GPU 资源也有很大的利用空间,同时引擎也为开发者提供了方便自由定制的材质系统。——Cocos 刘航

The following article is from 代码与画家 Author ituuz

前言


探照灯效果就是指整个场景或者图片都是黑的,只有灯光照射的地方才是亮的。实现方式有很多种,我们这里用 Shader 来实现,主要原因就是用 Shader 来实现,效率更高,效果更好,并且拓展性更强一些。下面是一个探照灯效果:



探照灯实现

光照原理


我们知道在程序中颜色由 RGB 三个数值来决定,比如红光的 RGB 值是 255、0、0,但在 GLSL 语言中表示颜色的最大值是 1.0,而且通常用一个 vec4 来表示,所以红光就是:vec4(1.0, 0.0, 0.0, 1.0); 第四个参数是 alpha 值,这里我们都设置为 1。

这里我们再说一下现实中我们看到物体有各式各样的颜色,并不是它们本身是这个颜色,而是它所能反射光的颜色。太阳光中是包含所有颜色光的,而一个物体能反射什么颜色的光与物体本身粒子特性有关,决定了它能反射光的波长范围,而其他范围的光则被吸收,所以我们就看到了各种颜色的物体。如下图所示:


利用这个原理,我们就可以通过 Shader 来控制目标的颜色显示来模拟光照了。

比如我们设定一个白色光照:vec4(1.0, 1.0, 1.0, 1.0)。该向量与片段着色器中的颜色相乘,因为每个值都是 1,所以物体颜色并不会改变。当我们设置为红光 vec4(1.0, 0.0, 0.0, 1.0)时,物体就会发红。

我们再控制光照的范围是一个圆形,那么就是一个探照灯效果了。

着色器实现


下面是着色器的实现过程,不是完整代码,只有代码片段,熟悉 GLSL 语言的同学应该可以看懂,不了解的同学可以直接到文末获取完整代码再回头过来学习亦可。


首先是光照范围逻辑,根据两点距离和光照中心点来判断是否在光照范围内,光照范围内的点为目标颜色,范围外的点则设置成黑色:



运行效果:



此时一个大体的光照范围效果就有了,但是不太自然,光照的边缘没有过渡,并且有锯齿存在,下面我们通过插值来把光照的边缘增加过渡效果:



smoothstep 函数接收三个参数,第一个是范围的最小值,第二个是范围的最大值,第三个参数是需要计算的目标值 x,x 小于最小值则返回 0,大于最大值返回 1,其余返回 0 到 1 的插值。上面代码里的范围是光照半径及半径加 0.1 的范围内进行插值来实现光线边缘的过渡效果。修改完再看,效果已经不错了。



最后我们通过代码来设置 light_center 参数就能实现光源的移动了:



细心的朋友可能发现,如果你的场景或者目标图片不是正方形,那么光照的范围可能就不是个正圆了,而是一个椭圆。


这是因为 OpenGL 中的坐标范围是从 -1 到 1,但宽高长度并不一定是等比的。所以我们在计算圆的范围时,可以把场景或者图片的宽高比传入进来,然后计算圆范围时进行修正就可以了。



进一步优化

漫反射环境光


大家知道,无论我们在多么漆黑的屋子里,总是能隐约看见物体轮廓的,这就是因为有漫反射的环境光造成的。


物体表面都不是光滑的,都是凹凸不平的,总会把光源的光线,反射到各个方向,其它物体接收到反射光后会再次反射,反复循环,这就是漫反射,我们也叫环境光


这就是为什么我们在没有光源的屋子里也能看见东西的原因,漫反射原理如下图:



在我们的例子中光线没有照到的地方漆黑一片,有时我们也需要一些漫反射造成的环境光。

真实的漫反射算法非常复杂,要通过法向量来运算,我们这里只是一个 2D 的纹理贴图,所以我们就把环境光设置成一个平均的常数就可以了,在光线没有照到的逻辑中加入环境光逻辑:


这时运行效果就和教程开头的效果基本一样了。


光照强度和光源颜色


前面的实现过程中,我们对在光照范围内的目标颜色没有干预,保证了目标的固有色。


但有时我们也需要改变一些,比如上面讲到了环境光,没有光照的地方并不是漆黑一片了,那么反过来,有光照的地方就一定是亮的吗?有时我们也需要调整一下光照的亮度和光照的颜色。


先看光照的亮度,这个比较简单,逻辑和环境光强度一样,我们再定义一个光源强度的参数就可以了:



我们将光照部分和参与插值运算的光照边缘过渡部分都乘上光照强度,就可以实现我们的目标效果了。


不过这里需要注意的一个问题是,光照强度 result 如果小于环境光强度 ambient_strength 会是什么效果呢?大家可以试一下,并尝试修改。


接下来增加光源颜色的支持。根据教程开头讲的光照原理,我们可以直接把之前我们例子中的目标颜色乘以一个光源颜色就可以了,光源颜色我们就用上面说过的红光:


运行效果如下:




完整代码:


https://github.com/yue19870813/cocos_demo/tree/master/CocosDemo_2_3_3/assets/case/searchlight






以上是由 Cocos 开发者 ituuz 分享的优质技术教程,此文同时参加了 Cocos 中文社区征稿活动,入选优秀稿件。欢迎各位开发者点击【阅读原文】查看原文,为作者点赞,与作者进行交流学习!


如果您在使用 Cocos 引擎的过程中,获得了独到的开发心得、见解或是方法,并且乐于分享出来,帮助更多开发者解决技术问题,加速游戏开发效率,期待您与我们联系!


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存